package com.ipan.builder.entity;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;

import com.ipan.builder.entity.annotation.Build;
import com.ipan.builder.entity.annotation.TableComment;

/**
 * JPA实体类配置信息
 * 
 * 实体名称还可以通过以下变量来访问：
 * ${name1} = AaaBbb
 * ${name2} = aaaBbb
 * ${name3} = aaa_bbb
 * ${name4} = aaa-bbb
 * ${name5} = aaabbb
 * ${name6} = AAABBB
 * 
 * 自定义接口的名称默认使用${name1}的名称；
 * 
 * 说明：只解析单一的JPA实体类，关联对象不做处理；
 * 
 * @author iPan
 * @version 2013-11-30
 */
public class JpaEntityCfg implements EntityDefineNaming {

	/** 名称格式：AaaBbb */
	private Class<?> entityClass;
	
	/** 实体名称 */
	private String entityName;

	/** @Table name */
	private String tableName = "";
	
	/** @Table catalog */
	private String tableCatalog = "";
	
	/** @Table schema */
	private String tableSchema = "";
	
	/** @TableComment */
	private String tableComment = "";
	
	/** 字段容器（实体字段名->字段配置） */
	private Map<String, JpaColumnCfg> columnContext = new HashMap<String, JpaColumnCfg>();
	/** 字段容器（数据库字段名->字段配置） */
	private Map<String, JpaColumnCfg> columnContext2 = new HashMap<String, JpaColumnCfg>();
	
	/** 自定义命名策略，默认返回name1格式； */
	private EntityDefineNaming naming = new EntityDefineNaming() {

		public String getDefName1() {
			return getName1();
		}

		public String getDefName2() {
			return getName1();
		}

		public String getDefName3() {
			return getName1();
		}

		public String getDefName4() {
			return getName1();
		}

		public String getDefName5() {
			return getName1();
		}
		
	};

	public JpaEntityCfg(Class<?> entityClass) {
		if (entityClass.getAnnotation(Entity.class) == null) {
			throw new RuntimeException("非JPA实体类：" + entityClass + ".");
		}
		this.entityClass = entityClass;
		this.entityName = entityClass.getSimpleName();
		
		// 解析@Table注释
		Table table = entityClass.getAnnotation(Table.class);
		if (table != null) {
			// 表名设置为小写
			if (StringUtils.isEmpty(table.name())) { // 默认使用类名以“_”分隔
				this.tableName = toSplitName(this.entityName, "_", true);
			} else {
				this.tableName = table.name().toLowerCase();
			}
			this.tableSchema = table.schema();
			this.tableCatalog = table.catalog();
		} else {
			this.tableName = toSplitName(this.entityName, "_", true);
		}
		// 解析@TableComment注释
		TableComment tableComment = entityClass.getAnnotation(TableComment.class);
		if (tableComment != null) {
			this.tableComment = tableComment.value();
		}
		// 解析所有字段
		List<Field> fieldList = getAllFields(entityClass);
		for (Field field : fieldList) {
			if (Modifier.isStatic(field.getModifiers())) {
				continue;
			}
			JpaColumnCfg columnCfg = new JpaColumnCfg(this, field.getName(), field.getType());
			// 解析@Column
			Column columnAnn = field.getAnnotation(Column.class);
			if (columnAnn != null) {
				// 字段名字设为小写
				if (StringUtils.isEmpty(columnAnn.name())) { // @Column name
					columnCfg.setName(toSplitName(field.getName(), "_", true));
				} else {
					columnCfg.setName(columnAnn.name().toLowerCase());
				}
				columnCfg.setUnique(columnAnn.unique()); // @Column unique
				columnCfg.setNullable(columnAnn.nullable()); // @Column nullable
				columnCfg.setInsertable(columnAnn.insertable()); // @Column insertable
				columnCfg.setUpdatable(columnAnn.updatable()); // @Column updatable
			    columnCfg.setColumnDefinition(columnAnn.columnDefinition()); // @Column columnDefinition
			    columnCfg.setTable(columnAnn.table()); // @Column table
			    columnCfg.setLength(columnAnn.length()); // @Column length
			    columnCfg.setPrecision(columnAnn.precision()); // @Column precision
			    columnCfg.setScale(columnAnn.scale()); // @Column scale
			} else {
				columnCfg.setName(toSplitName(field.getName(), "_", true));
			}
			
			// 解析@Build
			Build buildAnn = field.getAnnotation(Build.class);
			if (buildAnn != null) {
				columnCfg.setSearchColumn(buildAnn.searchColumn()); // 查询字段
				columnCfg.setListColumn(buildAnn.listColumn());	// 列表字段
				columnCfg.setSubmitColumn(buildAnn.submitColumn());	// 表单提交字段
				columnCfg.setHiddenColumn(buildAnn.hiddenColumn());	// 表单隐藏字段
				columnCfg.setComment(buildAnn.comment()); // 数据库字段注释
			}
			
			// 解析@Temporal
			Temporal temporalAnn = field.getAnnotation(Temporal.class);
			if (temporalAnn != null) {
				columnCfg.setTemporealType(temporalAnn.value());
			}
			
			// 解析@Transient
			Transient transientAnn = field.getAnnotation(Transient.class);
			if (transientAnn != null) {
				columnCfg.setTransient(true);
			}
			// 解析@OneToOne
			OneToOne oneToOneAnn = field.getAnnotation(OneToOne.class);
			if (oneToOneAnn != null) {
				columnCfg.setOneToOne(true);
			}
			// 解析@OneToMany
			OneToMany oneToManyAnn = field.getAnnotation(OneToMany.class);
			if (oneToManyAnn != null) {
				columnCfg.setOneToMany(true);
			}
			// 解析@ManyToOne
			ManyToOne manyToOneAnn = field.getAnnotation(ManyToOne.class);
			if (manyToOneAnn != null) {
				columnCfg.setManyToOne(true);
			}
			// 解析@ManyToMany
			ManyToMany manyToManyAnn = field.getAnnotation(ManyToMany.class);
			if (manyToManyAnn != null) {
				columnCfg.setManyToMany(true);
			}
			
			columnContext.put(columnCfg.getColumnName(), columnCfg);
			columnContext2.put(columnCfg.getName(), columnCfg);
		}
	}
	
	public List<JpaColumnCfg> getAllColumnCfg() {
		return new ArrayList<JpaColumnCfg>(columnContext.values());
	}
	
	/**
	 * 根据实体字段名获取
	 */
	public JpaColumnCfg getColumnCfg(String columnName) {
		return columnContext.get(columnName);
	}
	
	/**
	 * 根据数据库字段名获取（小写）
	 */
	public JpaColumnCfg getColumnCfgByName(String name) {
		return columnContext2.get(name);
	}
	
	public Class<?> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<?> entityClass) {
		this.entityClass = entityClass;
	}

	public String getEntityName() {
		return entityName;
	}

	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}

	/**
	 * 名称格式：AaaBbb
	 */
	public String getName1() {
		return this.entityName;
	}

	/**
	 * 名称格式：aaaBbb
	 */
	public String getName2() {
		return StringUtils.uncapitalize(this.entityName);
	}

	/**
	 * 名称格式：aaa_bbb
	 */
	public String getName3() {
		return toSplitName(this.entityName, "_", true);
	}
	
	/**
	 * 名称格式：aaa-bbb
	 */
	public String getName4() {
		return toSplitName(this.entityName, "-", true);
	}

	/**
	 * 名称格式：aaabbb
	 */
	public String getName5() {
		return this.entityName.toLowerCase();
	}

	/**
	 * 名称格式：AAABBB
	 */
	public String getName6() {
		return this.entityName.toUpperCase();
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public String getTableCatalog() {
		return tableCatalog;
	}

	public void setTableCatalog(String tableCatalog) {
		this.tableCatalog = tableCatalog;
	}

	public String getTableSchema() {
		return tableSchema;
	}

	public void setTableSchema(String tableSchema) {
		this.tableSchema = tableSchema;
	}
	
	public EntityDefineNaming getNaming() {
		return naming;
	}

	public void setNaming(EntityDefineNaming naming) {
		this.naming = naming;
	}

	public String getDefName1() {
		return this.naming.getDefName1();
	}

	public String getDefName2() {
		return this.naming.getDefName2();
	}

	public String getDefName3() {
		return this.naming.getDefName3();
	}

	public String getDefName4() {
		return this.naming.getDefName4();
	}

	public String getDefName5() {
		return this.naming.getDefName5();
	}

	public String getTableComment() {
		return tableComment;
	}

	public void setTableComment(String tableComment) {
		this.tableComment = tableComment;
	}
	
	private List<Field> getAllFields(Class<?> entityClass) {
		List<Field> fieldList = new ArrayList<Field>();
		for (Class<?> supperClass = entityClass; supperClass != Object.class; supperClass = supperClass.getSuperclass()) {
			Field[] fields = supperClass.getDeclaredFields();
			for (Field f : fields) {
				fieldList.add(f);
			}
		}
		Collections.reverse(fieldList);
		return fieldList;
	}
	
	private static String toSplitName(String name, String separator, boolean isLowerCase) {
		if (name == null || name.length() <= 1) {
			return name;
		}
		
		StringBuilder buf = new StringBuilder();
		buf.append(name.charAt(0));
		for (int i=1, len=name.length(); i<len; ++i) {
			char chr = name.charAt(i);
			if (Character.isUpperCase(chr)) {
				buf.append(separator);
			}
			buf.append(chr);
		}
		return (isLowerCase) ? buf.toString().toLowerCase() : buf.toString().toUpperCase();
	}
	
}
